package com.itheima;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.*;

/**
 * @Description: 主界面
 * @Author: Zhy
 * @Date: 2024/12/16 下午2:05
 * @Version: 1.0
 */
public class MainFrame extends JFrame {
	// 设置图片路径
	private static final String IMAGE_PATH = "stone_maze/src/image/";
	// 准备一个数组，用来存放数字色块的行列位置
	private final int[][] imageData = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 0}};
	// 定义变量，用来存放空白色块的位置
	private int row = 3; // 行
	private int col = 3; // 列

	// 准备一个数组，用于标识游戏结束的状态
	private final int[][] winArray = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 0}};
	// 定义变量，统计移动步数
	private int stepCount = 0;
	// 定义变量，保存最少移动步数
	private int minStepCount = 0;

	public MainFrame() {
		minStepCount = readMinStepCount();

		// 1. 初始化界面基本信息
		initFrame();
		// 4. 打乱数组的顺序
		initRandomArray();
		// 2. 初始化界面：展示数字色块
		initImage();
		// 3. 初始化系统菜单栏：展示退出、重启游戏等选项
		initMenu();

		// 5. 给窗口绑定上、下、左、右按键的事件监听器
		initKeyPressEvent();

		setVisible(true); // 当所有数据全都加载完成后，展示界面
	}

	/**
	 * 初始化界面基本信息
	 *
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:45
	 */
	private void initFrame() {
		setTitle("石头迷阵 v1.0"); // 设置标题
		setSize(465, 575); // 设置窗口大小
		setLocationRelativeTo(null); // 设置窗口居中显示
		// 设置关闭窗口时的操作
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		setLayout(null); // 设置绝对布局，方便后续设置位置
	}

	/**
	 * 打乱数组的顺序
	 *
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:46
	 */
	private void initRandomArray() {
		// 这种随机调换两个位置的方法，可能会导致最终无法得到正确的解法，还需改进
		// for (int i = 0; i < imageData.length; i++) {
		// 	for (int j = 0; j < imageData[i].length; j++) {
		// 		// 随机生成一个位置
		// 		int arrayX = (int) (Math.random() * imageData.length);
		// 		int arrayY = (int) (Math.random() * imageData[i].length);
		// 		// 交换位置
		// 		int temp = imageData[arrayX][arrayY];
		// 		imageData[arrayX][arrayY] = imageData[i][j];
		// 		imageData[i][j] = temp;
		// 	}
		// }

		// 改进后的随机打乱方法：让空白块多次随机上、下、左、右移动，类似于把魔方从拼好的状态进行打乱。
		for (int i = 0; i < 100; i++) {
			// 随机生成一个方向
			Direction direction = Direction.values()[(int) (Math.random() * 4)];
			// Direction.values() 将返回一个包含所有枚举值的数组。
			// 根据方向移动空白块
			switch (direction) {
				case UP:
					if (row > 0) {
						int temp = imageData[row][col];
						imageData[row][col] = imageData[row - 1][col];
						imageData[row - 1][col] = temp;
						row--;
					}
					break;
				case DOWN:
					if (row < imageData.length - 1) {
						int temp = imageData[row][col];
						imageData[row][col] = imageData[row + 1][col];
						imageData[row + 1][col] = temp;
						row++;
					}
					break;
				case LEFT:
					if (col > 0) {
						int temp = imageData[row][col];
						imageData[row][col] = imageData[row][col - 1];
						imageData[row][col - 1] = temp;
						col--;
					}
					break;
				case RIGHT:
					if (col < imageData.length - 1) {
						int temp = imageData[row][col];
						imageData[row][col] = imageData[row][col + 1];
						imageData[row][col + 1] = temp;
						col++;
					}
					break;
				default:
					break;
			}
		}

		// getBlankPosition(); // 获取空白色块的位置
	}

	/**
	 * 初始化界面：展示数字色块
	 *
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:46
	 */
	private void initImage() {
		// 先清空界面上的所有组件
		getContentPane().removeAll();

		// 展示移动步数
		JLabel stepLabel = new JLabel("移动步数：" + stepCount);
		stepLabel.setBounds(10, 10, 300, 20);
		stepLabel.setForeground(Color.RED);
		stepLabel.setFont(new Font("楷体", Font.PLAIN, 18));
		add(stepLabel);

		// 展示历史最小步数
		JLabel stepLabel2;
		if (minStepCount == 0) {
			stepLabel2 = new JLabel("历史最小步数：无");
		} else {
			stepLabel2 = new JLabel("历史最小步数：" + minStepCount);
		}
		stepLabel2.setBounds(290, 10, 300, 20);
		stepLabel2.setFont(new Font("楷体", Font.PLAIN, 18));
		add(stepLabel2);

		// 判断游戏是否结束
		if (isWin()) {
			// 展示游戏结束的图片
			JLabel label = new JLabel(new ImageIcon(IMAGE_PATH + "win.png"));
			label.setBounds(100, 200, 266, 88);
			add(label);

			// 更新最小步数
			minStepCount = readMinStepCount();
			if (minStepCount == 0 || stepCount < minStepCount) {
				writeDileScore(stepCount);
			}
		}

		// 1. 展示一个行列矩阵的图片色块铺满界面（4 * 4）
		for (int i = 0; i < imageData.length; i++) {
			for (int j = 0; j < imageData[i].length; j++) {
				// 拿到图片名称
				String imageName = IMAGE_PATH + imageData[i][j] + ".png";
				// 2. 创建一个JLabel，用来展示图片
				JLabel label = new JLabel();
				// 3. 设置图片路径
				label.setIcon(new ImageIcon(imageName));
				// 4. 设置JLabel的位置
				label.setBounds(j * 100 + 25, i * 100 + 80, 100, 100);
				// 5. 将JLabel添加到界面
				add(label);
			}
		}

		// 添加背景图片
		JLabel background = new JLabel(new ImageIcon(IMAGE_PATH + "background.png"));
		background.setBounds(2, 20, 450, 484);
		add(background);

		// 刷新界面，重新绘制
		repaint();
	}

	/**
	 * 设置系统菜单栏：展示退出、重启游戏等选项
	 *
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:46
	 */
	public void initMenu() {
		JMenuBar menuBar = new JMenuBar();
		JMenu menu = new JMenu("系统"); // 创建菜单

		// 创建菜单项
		JMenuItem exitItem = new JMenuItem("退出"); // 创建菜单项
		menu.add(exitItem); // 添加菜单项
		exitItem.addActionListener(e -> dispose()); // 销毁窗口

		JMenuItem restartItem = new JMenuItem("重启");
		menu.add(restartItem);
		restartItem.addActionListener(e -> {
			// 重置历史最小步数
			minStepCount = readMinStepCount();

			// 重新初始化游戏
			initRandomArray(); // 重新打乱生成数组
			initImage(); // 重新展示图片
			stepCount = 0; // 重置移动步数
		});

		menuBar.add(menu); // 添加菜单到菜单栏
		setJMenuBar(menuBar); // 添加菜单栏到界面
	}

	/**
	 * 给窗口绑定上、下、左、右按键的事件监听器
	 *
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:46
	 */
	private void initKeyPressEvent() {
		addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(java.awt.event.KeyEvent e) {
				// 获取当前按键的keyCode
				int keyCode = e.getKeyCode();
				// 判断keyCode是否是上下左右按键
				switch (keyCode) {
					case KeyEvent.VK_UP:
						switchAndMove(Direction.UP);
						break;
					case KeyEvent.VK_DOWN:
						switchAndMove(Direction.DOWN);
						break;
					case KeyEvent.VK_LEFT:
						switchAndMove(Direction.LEFT);
						break;
					case KeyEvent.VK_RIGHT:
						switchAndMove(Direction.RIGHT);
						break;
					default:
						break;
				}
			}
		});
	}

	/**
	 * 控制图片的移动
	 *
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:47
	 * @param: r 方向
	 */
	private void switchAndMove(Direction r) {
		switch (r) {
			case UP:
				// 上交换的条件：行数小于3
				if (row < imageData.length - 1) {
					// 交换位置
					int temp = imageData[row][col];
					imageData[row][col] = imageData[row + 1][col];
					imageData[row + 1][col] = temp;
					// 更新空白色块的位置
					row++;
					// 统计移动步数
					stepCount++;
				}
				break;
			case DOWN:
				// 下交换的条件：行数大于0
				if (row > 0) {
					// 交换位置
					int temp = imageData[row][col];
					imageData[row][col] = imageData[row - 1][col];
					imageData[row - 1][col] = temp;
					// 更新空白色块的位置
					row--;
					stepCount++;
				}
				break;
			case LEFT:
				// 左交换的条件：列数小于3
				if (col < imageData.length - 1) {
					// 交换位置
					int temp = imageData[row][col];
					imageData[row][col] = imageData[row][col + 1];
					imageData[row][col + 1] = temp;
					// 更新空白色块的位置
					col++;
					stepCount++;
				}
				break;
			case RIGHT:
				// 右交换的条件：列数大于0
				if (col > 0) {
					// 交换位置
					int temp = imageData[row][col];
					imageData[row][col] = imageData[row][col - 1];
					imageData[row][col - 1] = temp;
					// 更新空白色块的位置
					col--;
					stepCount++;
				}
				break;
			default:
				break;
		}

		// 重新展示图片
		initImage();
	}

	/**
	 * 获取空白色块的位置
	 *
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:47
	 */
	private void getBlankPosition() {
		for (int i = 0; i < imageData.length; i++) {
			for (int j = 0; j < imageData[i].length; j++) {
				if (imageData[i][j] == 0) {
					row = i;
					col = j;
					return;
				}
			}
		}
	}

	/**
	 * 判断游戏是否结束
	 *
	 * @return boolean 是否游戏结束
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:47
	 */
	public boolean isWin() {
		for (int i = 0; i < winArray.length; i++) {
			for (int j = 0; j < winArray[i].length; j++) {
				if (winArray[i][j] != imageData[i][j]) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 使用 IO 流读取最少步数
	 *
	 * @return int 最少步数
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:17
	 */
	private int readMinStepCount() {
		try (
			FileReader fr = new FileReader("stone_maze/src/score.txt");
			BufferedReader br = new BufferedReader(fr);
		) {
			String line = br.readLine();
			return Integer.parseInt(line);
		} catch (IOException e) {
			e.printStackTrace();
			return 0;
		}
	}

	/**
	 * 将当前最少步数写入文件
	 *
	 * @Author: Zhy
	 * @Date: 2024/12/26 12:23
	 * @param: count 最少步数
	 */
	public void writeDileScore(int count) {
		try (
			FileWriter fw = new FileWriter("stone_maze/src/score.txt");
			BufferedWriter bw = new BufferedWriter(fw);
		) {
			bw.write(String.valueOf(count));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
